﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Collections.Concurrent;

namespace MyLib
{
    /// <summary>
    /// 通用TCP客户端连接管理 和 FPSServerMgr 同上下文
    /// 基于同一个Actor 上下文来执行
    /// ActorSynchronizationContext
    /// </summary>
    public class TcpNetConnect
    {
        /// <summary>
        /// 初始化的时候需要设置
        /// 处理接收到的网络报文
        /// </summary>
        public ServerMsgReader.MessageHandler handler;
        public System.Action ClosedEvtHandler;
        public System.Action OnNetClosed;

        /// <summary>
        /// 主逻辑执行线程 需要Post逻辑到该线程来 类似于客户端的MainLoop
        /// 工作在FPSServerMgr 线程
        /// </summary>
        public ActorSynchronizationContext mainLoop;


        public enum State
        {
            Idle,
            Closed,
        }
        public State state = State.Idle;
        private byte[] mTemp = new byte[8192];

        public TcpClient tcpClient;
        //public TcpNetServer netServer;
        private NetworkStream dualStream;
        private List<ServerBundle> msgBuffers = new List<ServerBundle>();
        private ServerBundle curBundle;


        private enum SendState
        {
            Idle,
            InSend,
        }
        private SendState sendState = SendState.Idle;

        private ServerMsgReader msgReader = new ServerMsgReader();
        /// <summary>
        /// 初始化网络对象 
        /// </summary>
        public void Start()
        {
            LogHelper.Log2("TcpNetConnect:Start:");
            msgReader.msgHandle = HandleMsg;
            msgReader.flowHandler = flowHandler;
            msgReader.mainLoop = mainLoop;
            dualStream = tcpClient.GetStream();

            //开始接收报文
            GetPackets();
        }

        /// <summary>
        /// 外部调用发送报文
        /// 线程不安全
        /// 向客户端发送数据
        /// </summary>
        /// <param name="data"></param>
        /// <param name="bundle"></param>
        public async Task SendPacketAck(Google.ProtocolBuffers.IBuilderLite cmd, byte ackId = 0, byte flowId = 0)
        {
            await mainLoop;
            if (state == State.Closed) return;
            ServerBundle bundle;
            ServerBundle.sendImmediateError(cmd, ackId, flowId, 0, out bundle);
            //同时只能发送一个报文 防止报文混乱
            msgBuffers.Add(bundle);
            if(sendState == SendState.Idle)
            {
                Consume();
            }
        }

        private ConcurrentDictionary<byte, ServerMsgReader.MessageHandler> flowHandler = new ConcurrentDictionary<byte, ServerMsgReader.MessageHandler>();
        /// <summary>
        /// 发送序号
        /// AckId 响应序号
        /// </summary>
        private byte curFlowId = 0;
        /// <summary>
        /// 发送报文 等待响应报文
        /// 单向的FlowID 需要区分各自控制一个FlowId AckId需要区分一下
        /// </summary>
        /// <param name="cmd"></param>
        /// <param name="flowId"></param>
        /// <returns></returns>
        public async Task<KBEngine.Packet> SendWaitResponse(Google.ProtocolBuffers.IBuilderLite cmd)
        {
            await mainLoop;
            if (state == State.Closed) return null;
            curFlowId++;
            if (curFlowId == 0) curFlowId++;
            var flowId = curFlowId;

            KBEngine.Packet p = null;
            CancellationTokenSource source = new CancellationTokenSource();
            //MainLoop下执行
            flowHandler.TryAdd(flowId, (packet) =>
            {
                p = packet;
                source.Cancel(true);
                LogHelper.Log2("CancelToken:"+cmd.WeakBuild());
            });
            LogHelper.Log2("SendWaitRespon:"+flowHandler.Count+":"+flowId);
            SendPacketAck(cmd, 0, flowId);
            LogHelper.Log2("BeforeWaitTask:"+cmd.WeakBuild());
            try
            {
                await Task.Delay(10000, source.Token);
            }catch(Exception exp)
            {
                LogHelper.Log2("DelayFinishExp:"+exp.ToString());
            }
            LogHelper.Log2("AfterDelay:" + cmd.WeakBuild()+":"+source.IsCancellationRequested);
            if (!source.IsCancellationRequested)
            {
                LogHelper.Log2("SendWaitError:");
                throw new IOException("SendWaitError");
            }
            LogHelper.Log2("FinishSendWaitResp:"+cmd.WeakBuild()+":"+source.IsCancellationRequested);
            return p;
        }

        private IAsyncResult BeginSendPacket(AsyncCallback callback, object state)
        {
            return dualStream.BeginWrite(curBundle.coutStream.GetBuffer(), 0, curBundle.coutStream.position, callback, state);
        }
        private void EndSendPacket(IAsyncResult result)
        {
            dualStream.EndWrite(result);
        }
        /// <summary>
        /// 持续发送报文
        /// </summary>
        /// <returns></returns>
        private async Task Consume()
        {
            while (msgBuffers.Count > 0 && state != State.Closed)
            {
                ServerBundle toSend = null;
                toSend = msgBuffers[0];
                sendState = SendState.InSend;
                msgBuffers.RemoveAt(0);
                curBundle = toSend;
                //发送报文失败
                try
                {
                    LogHelper.Log2("ConsumePacket:"+curBundle.ackId+":"+curBundle.flowId+":"+curBundle.protoMsg);
                    await Task.Factory.FromAsync(BeginSendPacket, EndSendPacket, null);
                }catch(Exception exp)
                {
                    LogHelper.Log2("SendPacketExp:"+exp.ToString());
                    Close();
                }
                ServerBundle.ReturnBundle(curBundle);
                curBundle = null;
            }
            sendState = SendState.Idle;
        }

        /// <summary>
        /// FPSServerMgr 主线程执行接收网络报文
        /// </summary>
        /// <returns></returns>
        private async Task GetPackets()
        {
            await mainLoop;
            while (state == State.Idle)
            {
                try
                {
                    await GetOnePacket();
                }catch(Exception exp)
                {
                    LogHelper.Log2("ReceivePacketExp:"+exp.ToString());
                    Close();
                }
            }
        }

        /// <summary>
        /// 执行线程不确定
        /// 读取报文需要将
        /// </summary>
        /// <param name="callback"></param>
        /// <param name="state"></param>
        /// <returns></returns>
        private IAsyncResult BeginGetPacket(AsyncCallback callback, object state)
        {
            var result = dualStream.BeginRead(mTemp, 0, mTemp.Length, callback, state);
            return result;
        }

        /// <summary>
        /// 执行线程不确定
        /// </summary>
        /// <param name="result"></param>
        private void EndGetPacket(IAsyncResult result)
        {
            var bytes = 0;
            try
            {
                bytes = dualStream.EndRead(result);
            }catch(Exception exp)
            {
                LogHelper.Log2("EndGetPack:"+exp.ToString());
                Close();
                return;
            }

            if(bytes <= 0)
            {
                LogHelper.Log2("Connect Lost:"+state);
                Close();
                return;
            }
            msgReader.process(mTemp, (uint)bytes);
        }

        /// <summary>
        /// 线程不安全
        /// </summary>
        private void Close()
        {
            if (state == State.Closed) return;
            mainLoop.Post((s) =>
            {
                _Close();
            }, null);
        }

        /// <summary>
        /// 主线程处理
        /// </summary>
        private void _Close()
        {
            if (state == State.Closed) return;
            state = State.Closed;
            tcpClient.Close();
            tcpClient = null;
            if (ClosedEvtHandler != null) ClosedEvtHandler();
            if (OnNetClosed != null) OnNetClosed();
        }

        private async Task GetOnePacket()
        {
            await Task.Factory.FromAsync(BeginGetPacket, EndGetPacket, null);
        }

        /// <summary>
        /// 网络IO线程回调 在FPSServerMgr线程上执行
        /// </summary>
        /// <param name="packet"></param>
        private void HandleMsg(KBEngine.Packet packet)
        {
            LogHelper.Log2("TcpNetGetPacket:" + packet.protoBody);
            //mainLoop.Post((s) =>
            //MessageReader 已经切换到MainLoop来执行回调
            if (handler != null)
            {
                try
                {
                    LogHelper.Log2("TcpNetConnect:Receive:" + packet.flowId + ":" + packet.protoBody);
                    handler(packet);
                }
                catch (Exception exp)
                {
                    LogHelper.Log2("HandleMsgError:Tcp:" + exp.ToString());
                }
            }
        }
    }
}
